#ifndef __M_BINDING_H__
#define __M_BINDING_H__

#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>

namespace lywmq
{

struct Binding
{
    using ptr = std::shared_ptr<Binding>;
    std::string exchange_name;
    std::string msgqueue_name;
    std::string binding_key;

    Binding()
    {}
    Binding(const std::string& ename, const std::string& qname, const std::string& key)
    :exchange_name(ename), msgqueue_name(qname), binding_key(key)
    {}
};

// 队列名->绑定信息
using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
// 交换机->队列绑定信息
using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;
class BindingMapper
{
public:
    using ptr = std::shared_ptr<BindingMapper>;
    BindingMapper(const std::string& dbfile)
    :_sql_helper(dbfile)
    {
        std::string path = FileHelper::parentDirectory(dbfile);
        FileHelper::createDirectory(path);
        assert(_sql_helper.open());
        createTable();
    }
    void createTable()
    {
        std::stringstream sql;
        sql << "create table if not exists binding_table(";
        sql << "exchange_name varchar(32), ";
        sql << "msgqueue_name varchar(32), ";
        sql << "binding_key varchar(128));";
        assert(_sql_helper.exec(sql.str(), nullptr, nullptr));
    }
    void removeTable()
    {
        std::string sql = "drop table if exists binding_table;";
        _sql_helper.exec(sql, nullptr, nullptr);
    }
    bool insert(Binding::ptr& binding)
    {
        std::stringstream sql;
        sql << "insert into binding_table values(";
        sql << "'" << binding->exchange_name << "', ";
        sql << "'" << binding->msgqueue_name << "', ";
        sql << "'" << binding->binding_key << "');";
        return _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    void remove(const std::string& ename, const std::string& qname)
    {
        std::stringstream sql;
        sql << "delete from binding_table where ";
        sql << "exchange_name='" << ename << "' and ";
        sql << "msgqueue_name='" << qname << "';";
        _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    void removeExchangeBindings(const std::string& ename)
    {
        std::stringstream sql;
        sql << "delete from binding_table where ";
        sql << "exchange_name='" << ename << "';";
        _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    void removeMsgQueueBindings(const std::string& qname)
    {
        std::stringstream sql;
        sql << "delete from binding_table where ";
        sql << "msgqueue_name='" << qname << "';";
        _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    BindingMap recovery()
    {
        BindingMap result;
        std::string sql = "select exchange_name, msgqueue_name, binding_key from binding_table;";
        _sql_helper.exec(sql, selectCallback, &result);
        return result;
    }
private:
    static int selectCallback(void* arg, int numcol, char** row, char** fields)
    {
        BindingMap* result = (BindingMap*)arg;
        Binding::ptr bp = std::make_shared<Binding>(row[0], row[1], row[2]);
        MsgQueueBindingMap& qmap = (*result)[bp->exchange_name]; // 引用（会保证不存在则自动创建）
        qmap.insert(std::make_pair(bp->msgqueue_name, bp));
        return 0; // 表示正常返回，回调函数无异常
    }
private:
    SqliteHelper _sql_helper;
};

class BindingManager
{
public:
    using ptr = std::shared_ptr<BindingManager>;
    BindingManager(const std::string& dbfile)
    :_mapper(dbfile)
    {
        // 恢复数据
        _bindings = _mapper.recovery();
    }
    bool bind(const std::string& ename, const std::string& qname, const std::string& key, bool durable)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _bindings.find(ename);
        if (it != _bindings.end() && it->second.find(qname) != it->second.end())
            return true;
        Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
        if (durable)
        {
            bool ret = _mapper.insert(bp);
            if (ret == false) return false;
        }
        MsgQueueBindingMap& qmap = _bindings[ename];
        qmap.insert(std::make_pair(qname, bp));
        return true;
    }
    void unBind(const std::string& ename, const std::string& qname)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto eit = _bindings.find(ename);
        if (eit == _bindings.end()) return;
        auto qit = eit->second.find(qname);
        if (qit == eit->second.end()) return;
        _mapper.remove(ename, qname);
        _bindings[ename].erase(qname);
    }
    void removeExchangeBindings(const std::string& ename)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeExchangeBindings(ename);
        _bindings.erase(ename);
    }
    void removeMsgQueueBindings(const std::string& qname)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeMsgQueueBindings(qname);
        for (auto start = _bindings.begin(); start != _bindings.end(); start++)
        {
            // 遍历每个交换机的绑定信息并移除队列相关信息
            start->second.erase(qname);
        }
    }
    MsgQueueBindingMap getExchangeBindings(const std::string& ename)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto eit = _bindings.find(ename);
        if (eit == _bindings.end())
            return MsgQueueBindingMap();
        return eit->second;
    }
    Binding::ptr getBinding(const std::string& ename, const std::string& qname)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto eit = _bindings.find(ename);
        if (eit == _bindings.end())
            return Binding::ptr();
        auto qit = eit->second.find(qname);
        if (qit == eit->second.end()) 
            return Binding::ptr();
        return qit->second;
    }
    bool exists(const std::string& ename, const std::string& qname)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto eit = _bindings.find(ename);
        if (eit == _bindings.end())
            return false;
        auto qit = eit->second.find(qname);
        if (qit == eit->second.end()) 
            return false;
        return true;
    }
    size_t size()
    {
        size_t total_size = 0;
        std::unique_lock<std::mutex> lock(_mutex);
        for (auto start = _bindings.begin(); start != _bindings.end(); start++)
        {
            total_size += start->second.size();
        }
        return total_size;
    }
    void clear()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeTable();
        _bindings.clear();
    }
private:
    std::mutex _mutex;
    BindingMapper _mapper;
    BindingMap _bindings;
};

}

#endif